/**
 * PHOSIDE: PHosphorylation Site IDentification Engine.
 * Copyright 2009 Digital Biology Lab, University of Missouri.
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version. <p/> This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more
 * details.
 */

package phoside.data.feature.extaction;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import phoside.Protein;
import phoside.Proteins;
import phoside.PhosphoProteins.PhosphoType;
import phoside.ProteinFilter;

import phoside.data.feature.model.Instance;
import phoside.data.feature.model.InstanceImpl;
import phoside.data.feature.model.InstanceTag;
import phoside.data.feature.model.InstanceTagImpl;
import phoside.data.util.PhosphoDataUtil;

import phoside.util.StaticFinalValues;
import phoside.util.StringUtil;

/**
 *
 * @author gjj
 */
public class InstancesExtractorFromProteins implements InstancesExtractor {
    protected final Iterator<Protein> proteinIter;
    protected final List<FeatureExtractor> extractors;
    protected final Set<Character> aas;
    protected final int leftOffset;
    protected final int rightOffset;
    protected final PhosphoTag phosphoTag;
    protected final double extractRatio;
    protected ProteinFilter filter = null;

    public InstancesExtractorFromProteins(final Proteins proteins,
            final List<FeatureExtractor> extractors,
            final Set<PhosphoType> types,
            final PhosphoTag phosphoTag) {
        this(proteins, extractors, types, -1, phosphoTag, 1.0);
    }

    public InstancesExtractorFromProteins(final Proteins proteins,
            final List<FeatureExtractor> extractors,
            final Set<PhosphoType> types,
            final int windowOffset,
            final PhosphoTag phosphoTag) {
        this(proteins, extractors, types, windowOffset, phosphoTag, 1.0);
    }

    public InstancesExtractorFromProteins(final Proteins proteins,
            final List<FeatureExtractor> extractors,
            final Set<PhosphoType> types,
            final int offset,
            final PhosphoTag phosphoTag,
            final double extractRatio) {
        this(proteins, extractors, types, offset, offset, phosphoTag, extractRatio);
    }

    public InstancesExtractorFromProteins(final Proteins proteins,
            final List<FeatureExtractor> extractors,
            final Set<PhosphoType> types,
            final int leftOffset,
            final int rightOffset,
            final PhosphoTag phosphoTag) {
        this(proteins, extractors, types, leftOffset, rightOffset, phosphoTag, 1.0);
    }

    public InstancesExtractorFromProteins(final Proteins proteins,
            final List<FeatureExtractor> extractors,
            final Set<PhosphoType> types,
            final int leftOffset,
            final int rightOffset,
            final PhosphoTag phosphoTag,
            final double extractRatio) {
        this.proteinIter = proteins.proteinIterator();
        this.extractors = extractors;
        this.aas = PhosphoType.phosphorylatableAminoAcids(types);
        this.leftOffset = leftOffset;
        this.rightOffset = rightOffset;
        this.phosphoTag = phosphoTag;
        this.extractRatio = extractRatio;
    }

    public void setFilter(ProteinFilter filter) {
        this.filter = filter;
    }

    public boolean hasMore() {
        return proteinIter.hasNext();
    }

    /**
     *
     * @param n the number of entries to fetch, if n=-1, fetch all
     * @return
     */
    public List<Instance> fetch(int n) {
        List<Instance> instances = new ArrayList<Instance>();
        if (n<0) {
            while (hasMore()) {
                Protein protein = proteinIter.next();
                if (filter==null || filter.filter(protein))
                    instances.addAll(fetch(protein));
            }
        } else {
            for (int i=0; i<n && hasMore(); i++) {
                Protein protein = proteinIter.next();
                if (filter==null || filter.filter(protein))
                    instances.addAll(fetch(protein));
            }
        }
        return instances;
    }

    protected List<Instance> fetch(Protein protein) {
        List<Instance> instances = new ArrayList<Instance>();

        String proteinSeq = protein.getSequence();
        int lenSeq = proteinSeq.length();

        Set<Integer> sites = new TreeSet<Integer>();
        if (phosphoTag == PhosphoTag.BOTH) {
            sites.addAll(StringUtil.findAll(proteinSeq, aas, true));
        } else {
            Set<Integer> pSites = PhosphoDataUtil.getSites(protein);
            if (phosphoTag == PhosphoTag.PHOSPHO) {
                if (pSites==null) {
                    return instances;
                }
                for (int site : pSites) {
                    if (aas.contains(proteinSeq.charAt(site))) {
                        sites.add(site);
                    }
                }
            } else {
                sites.addAll(StringUtil.findAll(proteinSeq, aas, true));
                if (phosphoTag==PhosphoTag.NONPHOSPHO && pSites!=null) {
                    sites.removeAll(pSites);
                }
            }
        }

        for ( int site : sites) {
            if (Math.random()>=extractRatio)
                continue;

            if (leftOffset>=0 && rightOffset>=0) {
                int left = site - leftOffset;
                int right = site + rightOffset + 1;
                if (left<0 || right>lenSeq) { // ineligible
                    continue;
                }
            }

            InstanceTag tag = new InstanceTagImpl();
            tag.addTag(StaticFinalValues.INSTANCETAGNAMEPROTEIN, protein);
            tag.addTag(StaticFinalValues.INSTANCETAGNAMESITE, (Integer)site);
            Instance instance = new InstanceImpl(tag);
            instances.add(instance);

            if (extractors!=null) {
                for (FeatureExtractor extractor : extractors) {
                    extractor.extract(instance, true);
                }
            }
        }

        return instances;
    }

}
